home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / libs / gle / parseafm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-11-29  |  40.2 KB  |  1,191 lines

  1. /* parseAFM.c
  2.  *
  3.  * Copyright (c) 1988 Adobe Systems Incorporated.
  4.  * All Rights Reserved.
  5.  *
  6.  * This file is used in conjuction with the parseAFM.h header file.
  7.  * This file contains several procedures that are used to parse AFM
  8.  * files. It is intended to work with an application program that needs
  9.  * font metric information. The program can be used as is by making a
  10.  * procedure call to "parseFile" (passing in the expected parameters)
  11.  * and having it fill in a data structure with the data from the
  12.  * AFM file, or an application developer may wish to customize this
  13.  * code.
  14.  *
  15.  * There is also a file, parseAFMclient.c, that is a sample application
  16.  * showing how to call the "parseFile" procedure and how to use the data
  17.  * after "parseFile" has returned.
  18.  *
  19.  * Please read the comments in parseAFM.h and parseAFMclient.c.
  20.  *
  21.  * History:
  22.  *    original: DSM  Thu Oct 20 17:39:59 PDT 1988
  23.  */
  24. #include <stdlib.h>
  25. #include <stdio.h>
  26. #include <ctype.h>
  27. #include <string.h>
  28. #include <errno.h>
  29. #ifdef unix
  30. #include <sys/file.h>
  31. #endif
  32. #include <math.h>
  33. #include "parseafm.h"
  34. static char *token(FILE  *stream);
  35. static char *linetoken(FILE  *stream);
  36. /* static enum parseKey recognize(char *ident); */
  37. static BOOL parseGlobals(FILE *fp, GlobalFontInfo *gfi);
  38. static int initializeArray(FILE *fp, int *cwi);
  39. static parseCharWidths(FILE *fp, int *cwi);
  40. static parseCharMetrics(FILE *fp, FontInfo *fi);
  41. static parseTrackKernData(FILE *fp, FontInfo *fi);
  42. static parsePairKernData(FILE *fp, FontInfo *fi);
  43. static parseCompCharData(FILE *fp, FontInfo *fi);
  44. extern int parseFile (FILE *fp, FontInfo **fi, FLAGS flags);
  45.  
  46. #define lineterm EOL    /* line terminating character */
  47. #define normalEOF 1    /* return code from parsing routines used only */
  48.             /* in this module */
  49. #define Space "space"   /* used in string comparison to look for the width */
  50.             /* of the space character to initi the widths array */
  51. #define False "false"   /* used in string comparison to check the value of */
  52.             /* boolean keys (e.g. IsFixedPitch)  */
  53.  
  54. #define MATCH(A,B)    (strncmp((A),(B), MAX_NAME) == 0)
  55.  
  56.  
  57.  
  58.  
  59. /*************************** GLOBALS ***********************/
  60.  
  61. static char *ident = NULL; /* storage buffer for keywords */
  62.  
  63.  
  64. /* "shorts" for fast case statement
  65.  * The values of each of these enumerated items correspond to an entry in the
  66.  * table of strings defined below. Therefore, if you add a new string as
  67.  * new keyword into the keyStrings table, you must also add a corresponding
  68.  * parseKey AND it MUST be in the same position!
  69.  *
  70.  * IMPORTANT: since the sorting algorithm is a binary search, the strings of
  71.  * keywords must be placed in lexicographical order, below. [Therefore, the
  72.  * enumerated items are not necessarily in lexicographical order, depending
  73.  * on the name chosen. BUT, they must be placed in the same position as the
  74.  * corresponding key string.] The NOPE shall remain in the last position,
  75.  * since it does not correspond to any key string, and it is used in the
  76.  * "recognize" procedure to calculate how many possible keys there are.
  77.  */
  78.  
  79. static enum parseKey {
  80.   ASCENDER, CHARBBOX, CODE, COMPCHAR, CAPHEIGHT, COMMENT,
  81.   DESCENDER, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES,
  82.   ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN,
  83.   FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME, ISFIXEDPITCH,
  84.   ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, CHARNAME,
  85.   NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES,
  86.   STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS,
  87.   STARTTRACKKERN, TRACKKERN, UNDERLINEPOSITION,
  88.   UNDERLINETHICKNESS, VERSION, XYWIDTH, XWIDTH, WEIGHT, XHEIGHT,
  89.   NOPE };
  90.  
  91. /* keywords for the system:
  92.  * This a table of all of the current strings that are vaild AFM keys.
  93.  * Each entry can be referenced by the appropriate parseKey value (an
  94.  * enumerated data type defined above). If you add a new keyword here,
  95.  * a corresponding parseKey MUST be added to the enumerated data type
  96.  * defined above, AND it MUST be added in the same position as the
  97.  * string is in this table.
  98.  *
  99.  * IMPORTANT: since the sorting algorithm is a binary search, the keywords
  100.  * must be placed in lexicographical order. And, NULL should remain at the
  101.  * end.
  102.  */
  103.  
  104. static char *keyStrings[] = {
  105.   "Ascender", "B", "C", "CC", "CapHeight", "Comment",
  106.   "Descender", "EncodingScheme", "EndCharMetrics", "EndComposites",
  107.   "EndFontMetrics", "EndKernData", "EndKernPairs", "EndTrackKern",
  108.   "FamilyName", "FontBBox", "FontName", "FullName", "IsFixedPitch",
  109.   "ItalicAngle", "KP", "KPX", "L", "N",
  110.   "Notice", "PCC", "StartCharMetrics", "StartComposites",
  111.   "StartFontMetrics", "StartKernData", "StartKernPairs",
  112.   "StartTrackKern", "TrackKern", "UnderlinePosition",
  113.   "UnderlineThickness", "Version", "W", "WX", "Weight", "XHeight",
  114.   NULL };
  115.  
  116. /*************************** PARSING ROUTINES **************/
  117.  
  118. /*************************** token *************************/
  119.  
  120. /*  A "AFM File Conventions" tokenizer. That means that it will
  121.  *  return the next token delimited by white space.  See also
  122.  *  the `linetoken' routine, which does a similar thing but
  123.  *  reads all tokens until the next end-of-line.
  124.  */
  125.  
  126. static char *token(FILE *stream)
  127. {
  128.     int ch, idx;
  129.  
  130.     /* skip over white space */
  131.     while ((ch = fgetc(stream)) == ' ' || ch == lineterm ||
  132.             ch == ',' || ch == '\t' || ch == ';');
  133.  
  134.     idx = 0;
  135.     while (ch != EOF && ch != ' ' && ch != lineterm
  136.            && ch != '\t' && ch != ':' && ch != ';')
  137.     {
  138.         ident[idx++] = ch;
  139.         ch = fgetc(stream);
  140.     } /* while */
  141.  
  142.     if (ch == EOF && idx < 1) return ((char *)NULL);
  143.     if (idx >= 1 && ch != ':' ) ungetc(ch, stream);
  144.     if (idx < 1 ) ident[idx++] = ch;    /* single-character token */
  145.     ident[idx] = 0;
  146.  
  147.     return(ident);    /* returns pointer to the token */
  148.  
  149. } /* token */
  150.  
  151.  
  152. /*************************** linetoken *************************/
  153.  
  154. /*  "linetoken" will get read all tokens until the EOL character from
  155.  *  the given stream.  This is used to get any arguments that can be
  156.  *  more than one word (like Comment lines and FullName).
  157.  */
  158.  
  159. static char *linetoken(FILE *stream)
  160. {
  161.     int ch, idx;
  162.  
  163.     while ((ch = fgetc(stream)) == ' ' || ch == '\t' );
  164.  
  165.     idx = 0;
  166.     while (ch != EOF && ch != lineterm)
  167.     {
  168.         ident[idx++] = ch;
  169.         ch = fgetc(stream);
  170.     } /* while */
  171.  
  172.     ungetc(ch, stream);
  173.     ident[idx] = 0;
  174.  
  175.     return(ident);    /* returns pointer to the token */
  176.  
  177. } /* linetoken */
  178.  
  179.  
  180. /*************************** recognize *************************/
  181.  
  182. /*  This function tries to match a string to a known list of
  183.  *  valid AFM entries (check the keyStrings array above).
  184.  *  "ident" contains everything from white space through the
  185.  *  next space, tab, or ":" character.
  186.  *
  187.  *  The algorithm is a standard Knuth binary search.
  188.  */
  189.  
  190. static enum parseKey recognize(char *ident)
  191. {
  192.     int lower = 0, upper = (int) NOPE, midpoint, cmpvalue;
  193.     BOOL found = FALSE;
  194.  
  195.     while ((upper >= lower) && !found)
  196.     {
  197.         midpoint = (lower + upper)/2;
  198.         if (keyStrings[midpoint] == NULL) break;
  199.         cmpvalue = strncmp(ident, keyStrings[midpoint], MAX_NAME);
  200.         if (cmpvalue == 0) found = TRUE;
  201.         else if (cmpvalue < 0) upper = midpoint - 1;
  202.         else lower = midpoint + 1;
  203.     } /* while */
  204.  
  205.     if (found) return (enum parseKey) midpoint;
  206.     else return NOPE;
  207.  
  208. } /* recognize */
  209.  
  210.  
  211. /************************* parseGlobals *****************************/
  212.  
  213. /*  This function is called by "parseFile". It will parse the AFM File
  214.  *  up to the "StartCharMetrics" keyword, which essentially marks the
  215.  *  end of the Global Font Information and the beginning of the character
  216.  *  metrics information.
  217.  *
  218.  *  If the caller of "parseFile" specified that it wanted the Global
  219.  *  Font Information (as defined by the "AFM File Specification"
  220.  *  document), then that information will be stored in the returned
  221.  *  data structure.
  222.  *
  223.  *  Any Global Font Information entries that are not found in a
  224.  *  given file, will have the usual default initialization value
  225.  *  for its type (i.e. entries of type int will be 0, etc).
  226.  *
  227.  *  This function returns an error code specifying whether there was
  228.  *  a premature EOF or a parsing error. This return value is used by
  229.  *  parseFile to determine if there is more file to parse.
  230.  */
  231.  
  232. static BOOL parseGlobals(FILE *fp, GlobalFontInfo *gfi)
  233. {
  234.     BOOL cont = TRUE, save = (gfi != NULL);
  235.     int error = ok;
  236.     register char *keyword;
  237.  
  238.     while (cont)
  239.     {
  240.         keyword = token(fp);
  241.  
  242.         if (keyword == NULL)
  243.           /* Have reached an early and unexpected EOF. */
  244.           /* Set flag and stop parsing */
  245.         {
  246.             error = earlyEOF;
  247.             break;   /* get out of loop */
  248.         }
  249.         if (!save)
  250.           /* get tokens until the end of the Global Font info section */
  251.           /* without saving any of the data */
  252.             switch (recognize(keyword))
  253.             {
  254.                 case STARTCHARMETRICS:
  255.                     cont = FALSE;
  256.                     break;
  257.                 case ENDFONTMETRICS:
  258.                     cont = FALSE;
  259.                     error = normalEOF;
  260.                     break;
  261.                 default:
  262.                     break;
  263.             } /* switch */
  264.         else
  265.           /* otherwise parse entire global font info section, */
  266.           /* saving the data */
  267.             switch(recognize(keyword))
  268.             {
  269.                 case STARTFONTMETRICS:
  270.                     keyword = token(fp);
  271.             gfi->afmVersion = (char *) calloc(1, 1+strlen(keyword));
  272.                     strcpy(gfi->afmVersion, keyword);
  273.                     break;
  274.                 case COMMENT:
  275.                     keyword = linetoken(fp);
  276.                     break;
  277.                 case FONTNAME:
  278.                     keyword = token(fp);
  279.             gfi->fontName = (char *) calloc(1, 1+strlen(keyword));
  280.                     strcpy(gfi->fontName, keyword);
  281.                     break;
  282.                 case ENCODINGSCHEME:
  283.                     keyword = token(fp);
  284.             gfi->encodingScheme = (char *) calloc(1, 1+strlen(keyword));
  285.                     strcpy(gfi->encodingScheme, keyword);
  286.                     break;
  287.                 case FULLNAME:
  288.                     keyword = linetoken(fp);
  289.             gfi->fullName = (char *) calloc(1, 1+strlen(keyword));
  290.                     strcpy(gfi->fullName, keyword);
  291.                     break;
  292.                 case FAMILYNAME:
  293.                     keyword = linetoken(fp);
  294.             gfi->familyName = (char *) calloc(1, 1+strlen(keyword));
  295.                     strcpy(gfi->familyName, keyword);
  296.                     break;
  297.                 case WEIGHT:
  298.                     keyword = token(fp);
  299.             gfi->weight = (char *) calloc(1, 1+strlen(keyword));
  300.                     strcpy(gfi->weight, keyword);
  301.                     break;
  302.                 case ITALICANGLE:
  303.                     keyword = token(fp);
  304.                     gfi->italicAngle = atof(keyword);
  305.                     if (errno == ERANGE) {printf("Range error \n"); error = parseError;}
  306.                     break;
  307.                 case ISFIXEDPITCH:
  308.                     keyword = token(fp);
  309.                     if (MATCH(keyword, False))
  310.                         gfi->isFixedPitch = 0;
  311.                     else
  312.                         gfi->isFixedPitch = 1;
  313.                     break;
  314.             case UNDERLINEPOSITION:
  315.                     keyword = token(fp);
  316.                 gfi->underlinePosition = atoi(keyword);
  317.                     break;
  318.                 case UNDERLINETHICKNESS:
  319.                     keyword = token(fp);
  320.                     gfi->underlineThickness = atoi(keyword);
  321.                     break;
  322.                 case VERSION:
  323.                     keyword = token(fp);
  324.             gfi->version = (char *) calloc(1, 1+strlen(keyword));
  325.                     strcpy(gfi->version, keyword);
  326.                     break;
  327.                 case NOTICE:
  328.                     keyword = linetoken(fp);
  329.             gfi->notice = (char *) calloc(1, 1+strlen(keyword));
  330.                     strcpy(gfi->notice, keyword);
  331.                     break;
  332.                 case FONTBBOX:
  333.                     keyword = token(fp);
  334.                     gfi->fontBBox.llx = atoi(keyword);
  335.                     keyword = token(fp);
  336.                     gfi->fontBBox.lly = atoi(keyword);
  337.                     keyword = token(fp);
  338.                     gfi->fontBBox.urx = atoi(keyword);
  339.                     keyword = token(fp);
  340.                     gfi->fontBBox.ury = atoi(keyword);
  341.                     break;
  342.                 case CAPHEIGHT:
  343.                     keyword = token(fp);
  344.                     gfi->capHeight = atoi(keyword);
  345.                     break;
  346.                 case XHEIGHT:
  347.                     keyword = token(fp);
  348.                     gfi->xHeight = atoi(keyword);
  349.                     break;
  350.                 case DESCENDER:
  351.                     keyword = token(fp);
  352.                     gfi->descender = atoi(keyword);
  353.                     break;
  354.                 case ASCENDER:
  355.                     keyword = token(fp);
  356.                     gfi->ascender = atoi(keyword);
  357.                     break;
  358.                 case STARTCHARMETRICS:
  359.                     cont = FALSE;
  360.                     break;
  361.                 case ENDFONTMETRICS:
  362.                     cont = FALSE;
  363.                     error = normalEOF;
  364.                     break;
  365.                 case NOPE:
  366.                 default:
  367.             printf("Unrecognized key word {%s} \n",keyword);
  368.                     error = parseError;
  369.                     break;
  370.             } /* switch */
  371.     } /* while */
  372.  
  373.     return(error);
  374.  
  375. } /* parseGlobals */
  376.  
  377.  
  378.  
  379. /************************* initializeArray ************************/
  380.  
  381. /*  Unmapped character codes are (at Adobe Systems) assigned the
  382.  *  width of the space character (if one exists) else they get the
  383.  *  value of 250 ems. This function initializes all entries in the
  384.  *  char widths array to have this value. Then any mapped character
  385.  *  codes will be replaced with the width of the appropriate character
  386.  *  when parsing the character metric section.
  387.  
  388.  *  This function parses the Character Metrics Section looking
  389.  *  for a space character (by comparing character names). If found,
  390.  *  the width of the space character will be used to initialize the
  391.  *  values in the array of character widths.
  392.  *
  393.  *  Before returning, the position of the read/write pointer of the
  394.  *  file is reset to be where it was upon entering this function.
  395.  */
  396.  
  397. static int initializeArray(FILE *fp, int *cwi)
  398. {
  399.     BOOL cont = TRUE, found = FALSE;
  400.     long opos = ftell(fp);
  401.     int code = 0, width = 0, i = 0, error = 0;
  402.     register char *keyword;
  403.  
  404.     while (cont)
  405.     {
  406.         keyword = token(fp);
  407.         if (keyword == NULL)
  408.         {
  409.             error = earlyEOF;
  410.             break; /* get out of loop */
  411.         }
  412.         switch(recognize(keyword))
  413.         {
  414.             case COMMENT:
  415.                 keyword = linetoken(fp);
  416.                 break;
  417.             case CODE:
  418.                 code = atoi(token(fp));
  419.                 break;
  420.             case XWIDTH:
  421.                 width = atoi(token(fp));
  422.                 break;
  423.             case CHARNAME:
  424.                 keyword = token(fp);
  425.                 if (MATCH(keyword, Space))
  426.                 {
  427.                     cont = FALSE;
  428.                     found = TRUE;
  429.                 }
  430.                 break;
  431.             case ENDCHARMETRICS:
  432.                 cont = FALSE;
  433.                 break;
  434.             case ENDFONTMETRICS:
  435.                 cont = FALSE;
  436.                 error = normalEOF;
  437.                 break;
  438.             case NOPE:
  439.             default:
  440.         printf("Unrecognized key (level 1) word {%s} \n",keyword);
  441.                 error = parseError;
  442.                 break;
  443.         } /* switch */
  444.     } /* while */
  445.  
  446.     if (!found)
  447.     width = 250;
  448.  
  449.     for (i = 0; i < 256; ++i)
  450.         cwi[i] = width;
  451.  
  452.     fseek(fp, opos, 0);
  453.  
  454.     return(error);
  455.  
  456. } /* initializeArray */
  457.  
  458.  
  459. /************************* parseCharWidths **************************/
  460.  
  461. /*  This function is called by "parseFile". It will parse the AFM File
  462.  *  up to the "EndCharMetrics" keyword. It will save the character
  463.  *  width info (as opposed to all of the character metric information)
  464.  *  if requested by the caller of parseFile. Otherwise, it will just
  465.  *  parse through the section without saving any information.
  466.  *
  467.  *  If data is to be saved, parseCharWidths is passed in a pointer
  468.  *  to an array of widths that has already been initialized by the
  469.  *  standard value for unmapped character codes. This function parses
  470.  *  the Character Metrics section only storing the width information
  471.  *  for the encoded characters into the array using the character code
  472.  *  as the index into that array.
  473.  *
  474.  *  This function returns an error code specifying whether there was
  475.  *  a premature EOF or a parsing error. This return value is used by
  476.  *  parseFile to determine if there is more file to parse.
  477.  */
  478.  
  479. static parseCharWidths(FILE *fp, int *cwi)
  480. {
  481.     BOOL cont = TRUE, save = (cwi != NULL);
  482.     int pos = 0, error = ok;
  483.     register char *keyword;
  484.  
  485.     while (cont)
  486.     {
  487.         keyword = token(fp);
  488.           /* Have reached an early and unexpected EOF. */
  489.           /* Set flag and stop parsing */
  490.         if (keyword == NULL)
  491.         {
  492.             error = earlyEOF;
  493.             break; /* get out of loop */
  494.         }
  495.         if (!save)
  496.           /* get tokens until the end of the Char Metrics section without */
  497.           /* saving any of the data*/
  498.             switch (recognize(keyword))
  499.             {
  500.                 case ENDCHARMETRICS:
  501.                     cont = FALSE;
  502.                     break;
  503.                 case ENDFONTMETRICS:
  504.                     cont = FALSE;
  505.                     error = normalEOF;
  506.                     break;
  507.                 default:
  508.                     break;
  509.             } /* switch */
  510.         else
  511.           /* otherwise parse entire char metrics section, saving */
  512.           /* only the char x-width info */
  513.             switch(recognize(keyword))
  514.             {
  515.                 case COMMENT:
  516.                     keyword = linetoken(fp);
  517.                     break;
  518.                 case CODE:
  519.                     keyword = token(fp);
  520.                     pos = atoi(keyword);
  521.                     break;
  522.                 case XYWIDTH:
  523.                 /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */
  524.                     keyword = token(fp); keyword = token(fp); /* eat values */
  525.                     error = parseError;
  526.                     break;
  527.                 case XWIDTH:
  528.                     keyword = token(fp);
  529.                     if (pos >= 0) /* ignore unmapped chars */
  530.                         cwi[pos] = atoi(keyword);
  531.                     break;
  532.                 case ENDCHARMETRICS:
  533.                     cont = FALSE;
  534.                     break;
  535.                 case ENDFONTMETRICS:
  536.                     cont = FALSE;
  537.                     error = normalEOF;
  538.                     break;
  539.                 case CHARNAME:    /* eat values (so doesn't cause parseError) */
  540.                     keyword = token(fp);
  541.                     break;
  542.                 case CHARBBOX:
  543.                     keyword = token(fp); keyword = token(fp);
  544.                     keyword = token(fp); keyword = token(fp);
  545.             break;
  546.         case LIGATURE:
  547.                     keyword = token(fp); keyword = token(fp);
  548.             break;
  549.                 case NOPE:
  550.                 default:
  551.             printf("Unrecognized key (charstuff) word {%s} \n",keyword);
  552.                     error = parseError;
  553.                     break;
  554.             } /* switch */
  555.     } /* while */
  556.  
  557.     return(error);
  558.  
  559. } /* parseCharWidths */
  560.  
  561.  
  562. /************************* parseCharMetrics ************************/
  563.  
  564. /*  This function is called by parseFile if the caller of parseFile
  565.  *  requested that all character metric information be saved
  566.  *  (as opposed to only the character width information).
  567.  *
  568.  *  parseCharMetrics is passed in a pointer to an array of records
  569.  *  to hold information on a per character basis. This function
  570.  *  parses the Character Metrics section storing all character
  571.  *  metric information for the ALL characters (mapped and unmapped)
  572.  *  into the array.
  573.  *
  574.  *  This function returns an error code specifying whether there was
  575.  *  a premature EOF or a parsing error. This return value is used by
  576.  *  parseFile to determine if there is more file to parse.
  577.  */
  578.  
  579. static parseCharMetrics(FILE *fp, FontInfo *fi)
  580. {
  581.     BOOL cont = TRUE, firstTime = TRUE;
  582.     int error = ok, count = 0;
  583.     register CharMetricInfo *temp = fi->cmi;
  584.     register char *keyword;
  585.  
  586.     while (cont)
  587.     {
  588.         keyword = token(fp);
  589.         if (keyword == NULL)
  590.         {
  591.             error = earlyEOF;
  592.             break; /* get out of loop */
  593.         }
  594.         switch(recognize(keyword))
  595.         {
  596.             case COMMENT:
  597.                 keyword = linetoken(fp);
  598.                 break;
  599.             case CODE:
  600.                 if (count < fi->numOfChars)
  601.                 {
  602.                     if (firstTime) firstTime = FALSE;
  603.                     else temp++;
  604.                     temp->code = atoi(token(fp));
  605.                     count++;
  606.                 }
  607.                 else
  608.                 {
  609.             printf("Too many numofchars \n");
  610.                     error = parseError;
  611.                     cont = FALSE;
  612.                 }
  613.                 break;
  614.             case XYWIDTH:
  615.                 temp->wx = atoi(token(fp));
  616.                 temp->wy = atoi(token(fp));
  617.                 break;
  618.             case XWIDTH:
  619.                 temp->wx = atoi(token(fp));
  620.                 break;
  621.             case CHARNAME:
  622.                 keyword = token(fp);
  623.         temp->name = (char *) calloc(1, 1+strlen(keyword));
  624.                 strcpy(temp->name, keyword);
  625.                 break;
  626.             case CHARBBOX:
  627.                 temp->charBBox.llx = atoi(token(fp));
  628.                 temp->charBBox.lly = atoi(token(fp));
  629.                 temp->charBBox.urx = atoi(token(fp));
  630.                 temp->charBBox.ury = atoi(token(fp));
  631.                 break;
  632.             case LIGATURE: {
  633.                 Ligature **tail = &(temp->ligs);
  634.                 Ligature *node = *tail;
  635.  
  636.                 if (*tail != NULL)
  637.                 {
  638.                     while (node->next != NULL)
  639.                         node = node->next;
  640.                     tail = &(node->next);
  641.                 }
  642.  
  643.                 *tail = (Ligature *) calloc(1, sizeof(Ligature));
  644.                 keyword = token(fp);
  645.         (*tail)->succ = (char *) calloc(1, strlen(keyword)+1);
  646.                 strcpy((*tail)->succ, keyword);
  647.                 keyword = token(fp);
  648.         (*tail)->lig = (char *) calloc(1, strlen(keyword)+1);
  649.         strcpy((*tail)->lig, keyword);
  650. /*        printf("lig {%s} {%s} \n",(*tail)->lig,(*tail)->succ);
  651.         printf("ligd {%d} {%d} \n",(*tail)->lig,(*tail)->succ);
  652.   */
  653.               break; }
  654.             case ENDCHARMETRICS:
  655.                 cont = FALSE;;
  656.                 break;
  657.             case ENDFONTMETRICS:
  658.                 cont = FALSE;
  659.                 error = normalEOF;
  660.                 break;
  661.             case NOPE:
  662.             default:
  663.         printf("Unrecognized key (Top level) word {%s} \n",keyword);
  664.                 error = parseError;
  665.                 break;
  666.         } /* switch */
  667.     } /* while */
  668.  
  669.     if ((error == ok) && (count != fi->numOfChars)) {
  670.     printf("wrong num of chars got %d, wanted %d \n",count,fi->numOfChars);
  671.         error = parseError;
  672.     }
  673.     return(error);
  674.  
  675. } /* parseCharMetrics */
  676.  
  677.  
  678.  
  679. /************************* parseTrackKernData ***********************/
  680.  
  681. /*  This function is called by "parseFile". It will parse the AFM File
  682.  *  up to the "EndTrackKern" or "EndKernData" keywords. It will save the
  683.  *  track kerning data if requested by the caller of parseFile.
  684.  *
  685.  *  parseTrackKernData is passed in a pointer to the FontInfo record.
  686.  *  If data is to be saved, the FontInfo record will already contain
  687.  *  a valid pointer to storage for the track kerning data.
  688.  *
  689.  *  This function returns an error code specifying whether there was
  690.  *  a premature EOF or a parsing error. This return value is used by
  691.  *  parseFile to determine if there is more file to parse.
  692.  */
  693.  
  694. static parseTrackKernData(FILE *fp, FontInfo *fi)
  695. {
  696.     BOOL cont = TRUE, save = (fi->tkd != NULL);
  697.     int pos = 0, error = ok, tcount = 0;
  698.     register char *keyword;
  699.  
  700.     while (cont)
  701.     {
  702.         keyword = token(fp);
  703.  
  704.         if (keyword == NULL)
  705.         {
  706.             error = earlyEOF;
  707.             break; /* get out of loop */
  708.         }
  709.         if (!save)
  710.           /* get tokens until the end of the Track Kerning Data */
  711.           /* section without saving any of the data */
  712.             switch(recognize(keyword))
  713.             {
  714.                 case ENDTRACKKERN:
  715.                 case ENDKERNDATA:
  716.                     cont = FALSE;
  717.                     break;
  718.                 case ENDFONTMETRICS:
  719.                     cont = FALSE;
  720.                     error = normalEOF;
  721.                     break;
  722.                 default:
  723.                     break;
  724.             } /* switch */
  725.     else
  726.           /* otherwise parse entire Track Kerning Data section, */
  727.           /* saving the data */
  728.             switch(recognize(keyword))
  729.             {
  730.                 case COMMENT:
  731.                     keyword = linetoken(fp);
  732.                     break;
  733.                 case TRACKKERN:
  734.                     if (tcount < fi->numOfTracks)
  735.                     {
  736.                         keyword = token(fp);
  737.                         fi->tkd[pos].degree = atoi(keyword);
  738.                         keyword = token(fp);
  739.                         fi->tkd[pos].minPtSize = atof(keyword);
  740.                         if (errno == ERANGE) error = parseError;
  741.                         keyword = token(fp);
  742.                         fi->tkd[pos].minKernAmt = atof(keyword);
  743.                         if (errno == ERANGE) error = parseError;
  744.                         keyword = token(fp);
  745.                         fi->tkd[pos].maxPtSize = atof(keyword);
  746.                         if (errno == ERANGE) error = parseError;
  747.                         keyword = token(fp);
  748.                         fi->tkd[pos++].maxKernAmt = atof(keyword);
  749.                         if (errno == ERANGE) error = parseError;
  750.                         tcount++;
  751.                     }
  752.                     else
  753.                     {
  754.             printf("Too many tracks \n");
  755.                         error = parseError;
  756.                         cont = FALSE;
  757.                     }
  758.                     break;
  759.                 case ENDTRACKKERN:
  760.                 case ENDKERNDATA:
  761.                     cont = FALSE;
  762.                     break;
  763.                 case ENDFONTMETRICS:
  764.                     cont = FALSE;
  765.                     error = normalEOF;
  766.                     break;
  767.                 case NOPE:
  768.                 default:
  769.         printf("Unrecognized key (zzz level) word {%s} \n",keyword);
  770.                     break;
  771.             } /* switch */
  772.     } /* while */
  773.  
  774.     if (error == ok && tcount != fi->numOfTracks) {
  775.         error = parseError;
  776.     printf("Wrong num of tracks %d \n",tcount);
  777.     }
  778.     return(error);
  779.  
  780. } /* parseTrackKernData */
  781.  
  782.  
  783. /************************* parsePairKernData ************************/
  784.  
  785. /*  This function is called by "parseFile". It will parse the AFM File
  786.  *  up to the "EndKernPairs" or "EndKernData" keywords. It will save
  787.  *  the pair kerning data if requested by the caller of parseFile.
  788.  *
  789.  *  parsePairKernData is passed in a pointer to the FontInfo record.
  790.  *  If data is to be saved, the FontInfo record will already contain
  791.  *  a valid pointer to storage for the pair kerning data.
  792.  *
  793.  *  This function returns an error code specifying whether there was
  794.  *  a premature EOF or a parsing error. This return value is used by
  795.  *  parseFile to determine if there is more file to parse.
  796.  */
  797.  
  798. static parsePairKernData(FILE *fp, FontInfo *fi)
  799. {
  800.     BOOL cont = TRUE, save = (fi->pkd != NULL);
  801.     int pos = 0, error = ok, pcount = 0;
  802.     register char *keyword;
  803.  
  804.     while (cont)
  805.     {
  806.         keyword = token(fp);
  807.  
  808.         if (keyword == NULL)
  809.         {
  810.             error = earlyEOF;
  811.             break; /* get out of loop */
  812.         }
  813.         if (!save)
  814.           /* get tokens until the end of the Pair Kerning Data */
  815.           /* section without saving any of the data */
  816.             switch(recognize(keyword))
  817.             {
  818.                 case ENDKERNPAIRS:
  819.                 case ENDKERNDATA:
  820.                     cont = FALSE;
  821.                     break;
  822.                 case ENDFONTMETRICS:
  823.                     cont = FALSE;
  824.                     error = normalEOF;
  825.                     break;
  826.                 default:
  827.                     break;
  828.             } /* switch */
  829.     else
  830.           /* otherwise parse entire Pair Kerning Data section, */
  831.           /* saving the data */
  832.             switch(recognize(keyword))
  833.             {
  834.                 case COMMENT:
  835.                     keyword = linetoken(fp);
  836.                     break;
  837.                 case KERNPAIR:
  838.                     if (pcount < fi->numOfPairs)
  839.                     {
  840.                         keyword = token(fp);
  841.                         fi->pkd[pos].name1 = (char *)
  842.                 calloc(1,1+ strlen(keyword));
  843.                         strcpy(fi->pkd[pos].name1, keyword);
  844.                         keyword = token(fp);
  845.                         fi->pkd[pos].name2 = (char *)
  846.                 calloc(1, 1+strlen(keyword));
  847.                         strcpy(fi->pkd[pos].name2, keyword);
  848.                         keyword = token(fp);
  849.                         fi->pkd[pos].xamt = atoi(keyword);
  850.                         keyword = token(fp);
  851.                         fi->pkd[pos++].yamt = atoi(keyword);
  852.                         pcount++;
  853.                     }
  854.                     else
  855.                     {
  856.             printf("Too many pairs \n");
  857.                         error = parseError;
  858.                         cont = FALSE;
  859.                     }
  860.                     break;
  861.                 case KERNPAIRXAMT:
  862.                     if (pcount < fi->numOfPairs)
  863.                     {
  864.                         keyword = token(fp);
  865.                         fi->pkd[pos].name1 = (char *)
  866.                             calloc(1, strlen(keyword));
  867.                         strcpy(fi->pkd[pos].name1, keyword);
  868.                         keyword = token(fp);
  869.                         fi->pkd[pos].name2 = (char *)
  870.                             calloc(1, 1+strlen(keyword));
  871.                         strcpy(fi->pkd[pos].name2, keyword);
  872.                         keyword = token(fp);
  873.                         fi->pkd[pos++].xamt = atoi(keyword);
  874.                         pcount++;
  875.                     }
  876.                     else
  877.                     {
  878.             printf("Too many pairs2 \n");
  879.                         error = parseError;
  880.                         cont = FALSE;
  881.                     }
  882.                     break;
  883.                 case ENDKERNPAIRS:
  884.                 case ENDKERNDATA:
  885.                     cont = FALSE;
  886.                     break;
  887.                 case ENDFONTMETRICS:
  888.                     cont = FALSE;
  889.                     error = normalEOF;
  890.                     break;
  891.                 case NOPE:
  892.                 default:
  893.         printf("Unrecognized key (Top level aas) word {%s} \n",keyword);
  894.         error = parseError;
  895.                     break;
  896.             } /* switch */
  897.     } /* while */
  898.  
  899.     if (error == ok && pcount != fi->numOfPairs) {
  900.     printf("wrong number of pcountss \n");
  901.         error = parseError;
  902.     }
  903.  
  904.     return(error);
  905.  
  906. } /* parsePairKernData */
  907.  
  908.  
  909. /************************* parseCompCharData **************************/
  910.  
  911. /*  This function is called by "parseFile". It will parse the AFM File
  912.  *  up to the "EndComposites" keyword. It will save the composite
  913.  *  character data if requested by the caller of parseFile.
  914.  *
  915.  *  parseCompCharData is passed in a pointer to the FontInfo record, and
  916.  *  a boolean representing if the data should be saved.
  917.  *
  918.  *  This function will create the appropriate amount of storage for
  919.  *  the composite character data and store a pointer to the storage
  920.  *  in the FontInfo record.
  921.  *
  922.  *  This function returns an error code specifying whether there was
  923.  *  a premature EOF or a parsing error. This return value is used by
  924.  *  parseFile to determine if there is more file to parse.
  925.  */
  926.  
  927. static parseCompCharData(FILE *fp, FontInfo *fi)
  928. {
  929.     BOOL cont = TRUE, firstTime = TRUE, save = (fi->ccd != NULL);
  930.     int pos = 0, j = 0, error = ok, ccount = 0, pcount = 0;
  931.     register char *keyword;
  932.  
  933.     while (cont)
  934.     {
  935.         keyword = token(fp);
  936.         if (keyword == NULL)
  937.           /* Have reached an early and unexpected EOF. */
  938.           /* Set flag and stop parsing */
  939.         {
  940.             error = earlyEOF;
  941.             break; /* get out of loop */
  942.         }
  943.         if (ccount > fi->numOfComps)
  944.         {
  945.             error = parseError;
  946.             break; /* get out of loop */
  947.         }
  948.         if (!save)
  949.           /* get tokens until the end of the Composite Character info */
  950.           /* section without saving any of the data */
  951.             switch(recognize(keyword))
  952.             {
  953.                 case ENDCOMPOSITES:
  954.                     cont = FALSE;
  955.                     break;
  956.                 case ENDFONTMETRICS:
  957.                     cont = FALSE;
  958.                     error = normalEOF;
  959.                     break;
  960.                 default:
  961.                     break;
  962.             } /* switch */
  963.     else
  964.           /* otherwise parse entire Composite Character info section, */
  965.           /* saving the data */
  966.             switch(recognize(keyword))
  967.             {
  968.                 case COMMENT:
  969.                     keyword = linetoken(fp);
  970.                     break;
  971.                 case COMPCHAR:
  972.                     if (ccount < fi->numOfComps)
  973.                     {
  974.                         keyword = token(fp);
  975.                         if (pcount != fi->ccd[pos].numOfPieces)
  976.                             error = parseError;
  977.                         pcount = 0;
  978.                         if (firstTime) firstTime = FALSE;
  979.                         else pos++;
  980.                         fi->ccd[pos].ccName = (char *)
  981.                             calloc(1, 1+strlen(keyword));
  982.                         strcpy(fi->ccd[pos].ccName, keyword);
  983.                         keyword = token(fp);
  984.                         fi->ccd[pos].numOfPieces = atoi(keyword);
  985.                         fi->ccd[pos].pieces = (Pcc *)
  986.                             calloc(fi->ccd[pos].numOfPieces, sizeof(Pcc));
  987.                         j = 0;
  988.                         ccount++;
  989.                     }
  990.                     else
  991.                     {
  992.                         error = parseError;
  993.                         cont = FALSE;
  994.                     }
  995.                     break;
  996.                 case COMPCHARPIECE:
  997.                     if (pcount < fi->ccd[pos].numOfPieces)
  998.                     {
  999.                         keyword = token(fp);
  1000.                         fi->ccd[pos].pieces[j].pccName = (char *)
  1001.                                 calloc(1, 1+strlen(keyword));
  1002.                         strcpy(fi->ccd[pos].pieces[j].pccName, keyword);
  1003.                         keyword = token(fp);
  1004.                         fi->ccd[pos].pieces[j].deltax = atoi(keyword);
  1005.                         keyword = token(fp);
  1006.                         fi->ccd[pos].pieces[j++].deltay = atoi(keyword);
  1007.                         pcount++;
  1008.                     }
  1009.                     else
  1010.                         error = parseError;
  1011.                     break;
  1012.                 case ENDCOMPOSITES:
  1013.                     cont = FALSE;
  1014.                     break;
  1015.                 case ENDFONTMETRICS:
  1016.                     cont = FALSE;
  1017.                     error = normalEOF;
  1018.                     break;
  1019.                 case NOPE:
  1020.                 default:
  1021.                     error = parseError;
  1022.                     break;
  1023.             } /* switch */
  1024.     } /* while */
  1025.  
  1026.     if (error == ok && ccount != fi->numOfComps)
  1027.         error = parseError;
  1028.  
  1029.     return(error);
  1030.  
  1031. } /* parseCompCharData */
  1032.  
  1033.  
  1034.  
  1035.  
  1036. /*************************** 'PUBLIC' FUNCTION ********************/
  1037.  
  1038.  
  1039. /*************************** parseFile *****************************/
  1040.  
  1041. /*  parseFile is the only 'public' procedure available. It is called
  1042.  *  from an application wishing to get information from an AFM file.
  1043.  *  The caller of this function is responsible for locating and opening
  1044.  *  an AFM file and handling all errors associated with that task.
  1045.  *
  1046.  *  parseFile expects 3 parameters: a vaild file pointer, a pointer
  1047.  *  to a (FontInfo *) variable (for which storage will be allocated and
  1048.  *  the data requested filled in), and a mask specifying which
  1049.  *  data from the AFM File should be saved in the FontInfo structure.
  1050.  *
  1051.  *  The file will be parsed and the requested data will be stored in
  1052.  *  a record of type FontInfo (refer to ParseAFM.h).
  1053.  *
  1054.  *  parseFile returns an error code as defined in parseAFM.h.
  1055.  *
  1056.  *  The position of the read/write pointer associated with the file
  1057.  *  pointer upon return of this function is undefined.
  1058.  */
  1059.  
  1060. extern int parseFile (FILE *fp, FontInfo **fi, FLAGS flags)
  1061. {
  1062.  
  1063.     int code = ok;     /* return code from each of the parsing routines */
  1064.     int error = ok;    /* used as the return code from this function */
  1065.  
  1066.     register char *keyword; /* used to store a token */
  1067.  
  1068.  
  1069.     /* storage data for the global variable ident */
  1070.     ident = (char *) calloc(MAX_NAME, sizeof(char));
  1071.  
  1072.     (*fi) = (FontInfo *) calloc(1, sizeof(FontInfo));
  1073.  
  1074.     if (flags & P_G)
  1075.         (*fi)->gfi = (GlobalFontInfo *) calloc(1, sizeof(GlobalFontInfo));
  1076.  
  1077.     /* The AFM File begins with Global Font Information. This section */
  1078.     /* will be parsed whether or not information should be saved. */
  1079.     code = parseGlobals(fp, (*fi)->gfi);
  1080.  
  1081.     if (code < 0) error = code;
  1082.  
  1083.     /* The Global Font Information is followed by the Character Metrics */
  1084.     /* section. Which procedure is used to parse this section depends on */
  1085.     /* how much information should be saved. If all of the metrics info */
  1086.     /* is wanted, parseCharMetrics is called. If only the character widths */
  1087.     /* is wanted, parseCharWidths is called. parseCharWidths will also */
  1088.     /* be called in the case that no character data is to be saved, just */
  1089.     /* to parse through the section. */
  1090.  
  1091.     if ((code != normalEOF) && (code != earlyEOF))
  1092.     {
  1093.         (*fi)->numOfChars = atoi(token(fp));
  1094.     if (flags & (P_M ^ P_W))
  1095.         {
  1096.             (*fi)->cmi = (CharMetricInfo *)
  1097.                       calloc((*fi)->numOfChars, sizeof(CharMetricInfo));
  1098.             code = parseCharMetrics(fp, *fi);
  1099.         }
  1100.         else
  1101.         {
  1102.             if (flags & P_W)
  1103.                 (*fi)->cwi = (int *) calloc(256, sizeof(int));
  1104.             /* parse section regardless */
  1105.             code = parseCharWidths(fp, (*fi)->cwi);
  1106.         } /* else */
  1107.     } /* if */
  1108.  
  1109.     if ((error != earlyEOF) && (code < 0)) error = code;
  1110.  
  1111.     /* The remaining sections of the AFM are optional. This code will */
  1112.     /* look at the next keyword in the file to determine what section */
  1113.     /* is next, and then allocate the appropriate amount of storage */
  1114.     /* for the data (if the data is to be saved) and call the */
  1115.     /* appropriate parsing routine to parse the section. */
  1116.  
  1117.     while ((code != normalEOF) && (code != earlyEOF))
  1118.     {
  1119.         keyword = token(fp);
  1120.         if (keyword == NULL)
  1121.           /* Have reached an early and unexpected EOF. */
  1122.           /* Set flag and stop parsing */
  1123.         {
  1124.             code = earlyEOF;
  1125.             break; /* get out of loop */
  1126.         }
  1127.         switch(recognize(keyword))
  1128.         {
  1129.             case STARTKERNDATA:
  1130.                 break;
  1131.             case ENDKERNDATA:
  1132.                 break;
  1133.             case STARTTRACKKERN:
  1134.                 keyword = token(fp);
  1135.                 if (flags & P_T)
  1136.                 {
  1137.                     (*fi)->numOfTracks = atoi(keyword);
  1138.                     (*fi)->tkd = (TrackKernData *)
  1139.                         calloc((*fi)->numOfTracks, sizeof(TrackKernData));
  1140.                 } /* if */
  1141.                 code = parseTrackKernData(fp, *fi);
  1142.                 break;
  1143.             case STARTKERNPAIRS:
  1144.                 keyword = token(fp);
  1145.                 if (flags & P_P)
  1146.                 {
  1147.                     (*fi)->numOfPairs = atoi(keyword);
  1148.                     (*fi)->pkd = (PairKernData *)
  1149.                         calloc((*fi)->numOfPairs, sizeof(PairKernData));
  1150.                 } /* if */
  1151.                 code = parsePairKernData(fp, *fi);
  1152.                 break;
  1153.             case STARTCOMPOSITES:
  1154.                 keyword = token(fp);
  1155.                 if (flags & P_C)
  1156.                 {
  1157.                     (*fi)->numOfComps = atoi(keyword);
  1158.                     (*fi)->ccd = (CompCharData *)
  1159.                         calloc((*fi)->numOfComps, sizeof(CompCharData));
  1160.                 } /* if */
  1161.                 code = parseCompCharData(fp, *fi);
  1162.                 break;
  1163.             case ENDFONTMETRICS:
  1164.                 code = normalEOF;
  1165.                 break;
  1166.             case NOPE:
  1167.             default:
  1168.                 code = parseError;
  1169.                 break;
  1170.         } /* switch */
  1171.  
  1172.         if ((error != earlyEOF) && (code < 0)) error = code;
  1173.  
  1174.     } /* while */
  1175.  
  1176.     if ((error != earlyEOF) && (code < 0)) error = code;
  1177.  
  1178.     return(error);
  1179.  
  1180. } /* parseFile */
  1181.  
  1182.  
  1183.  
  1184.  
  1185.  
  1186.  
  1187.  
  1188.  
  1189.  
  1190.  
  1191.